this的指向
this绑定规则一共有5种:
- 默认绑定(严格/非严格模式)
- 隐式绑定
- 显式绑定
- new绑定
- 箭头函数绑定
普通方法的this指向(默认绑定 严格/非严格模式)
1 | // 非严格模式 |
1 | // 非严格模式 |
这个例子中let
没有给顶层对象中(浏览器是window)添加属性,window.name2和window.doSth
都是undefined
。
严格模式中,普通函数中的this
则表现不同,表现为undefined
。
1 | // 严格模式 |
对象方法的this指向(隐式绑定)
this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象
- 如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window
- 如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象
- 如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象
1 | var person = { |
构造函数的this指向(new绑定)
1 | function Foo(name,age){ |
构造函数的 this 指向实例的,因为 new 的步骤
- 创建一个新的空对象
- 新对象的 __proto__ 属性指向构造函数的原型对象
- 把构造函数的this设置为新创建的对象
- 执行构造函数的代码
- 构造函数没有return语句,则将该新创建的对象返回
new 的模拟实现
1 | function myNew(Func, ...args) { |
优化版
__proto__
在IE浏览器中不支持,使用 Object.create(),作用如下
1 | let x = { name: "lsh" }; |
1 | function myNew(Func, ...args) { |
Object.create() 的模拟
1 | function newCreate(prototype, propertiesObject) { |
原型链中的this指向
1 | function Student(name){ |
会发现这个似曾相识。这就是对象上的方法调用模式。自然是指向生成的新对象。 如果该对象继承自其它对象。同样会通过原型链查找。 上面代码使用 ES6
中class
写法则是:
1 | class Student{ |
当this碰到return
如果返回值是一个对象({},[],RegExp, Date, Function),那么this指向的就是那个返回的对象,如果返回值不是一个对象(基本类型)那么this还是指向函数的实例
1 | function Foo(name,age){ |
箭头函数的this指向(箭头函数绑定)
箭头函数本身没有
this
、super
、arguments
和new.target
绑定不能使用
new
来调用没有原型对象
不可以改变
this
的绑定形参名称不能重复
箭头函数中没有this
绑定,必须通过查找作用域链来决定其值。 如果箭头函数被非箭头函数包含,则this
绑定的是最近一层非箭头函数的this
,否则this
的值则被设置为全局对象。在定义函数的时候绑定,而不是在执行函数的时候绑定。
1 | var name = 'window'; |
其实就是相当于箭头函数外的this
是缓存的该箭头函数上层的普通函数的this
。如果没有普通函数,则是全局对象(浏览器中则是window
)。 也就是说无法通过call
、apply
、bind
绑定箭头函数的this
(它自身没有this
)。而call
、apply
、bind
可以绑定缓存箭头函数上层的普通函数的this
。
1 | var student = { |
call、apply、bind 的 this 指向(显示绑定)
在另一篇文章中
DOM事件处理函数的this指向
addEventerListener、attachEvent、onclick。this 指向触发事件的元素,也就是始事件处理程序所绑定到的DOM节点。
1 | var ele = document.getElementById("id"); |
HTML标签内联事件处理函数的this指向
this 指向所在的DOM元素。第一个是button
本身,所以是true
,第二个是window
。这里跟严格模式没有关系。
1 | <button class="btn1" onclick="console.log(this === document.querySelector('.btn1'))">点我呀</button> |
jQuery的this指向
在许多情况下JQuery的 this 都指向DOM元素节点
1 | $(".btn").on("click",function(){ |
优先级
new
调用 > call、apply、bind
调用 > 对象上的函数调用 > 普通函数调用
1 | var name = 'window'; |
new
和 apply、call
无法一起使用,这是因为函数内部有两个不同的方法:[[Call]]
和[[Constructor]]
。 当使用普通函数调用时,[[Call]]
会被执行。当使用构造函数调用时,[[Constructor]]
会被执行。call
、apply
、bind
和箭头函数内部没有[[Constructor]]
方法。
下面比较 new 和 call、apply、bind
调用。在《你不知道的JavaScript》是对比bind
和new
,引用了mdn
的bind
的ployfill
实现,new
调用时bind之后的函数,会忽略bind
绑定的第一个参数。
1 | function foo(something) { |
bar 被绑定到 obj1 上,但是 new bar(3) 并没有像我们预计的那样把 obj1.a 修改为3。但是,new 修改了绑定调用 bar() 中的 this。
总结
如果要判断一个函数的this
绑定,就需要找到这个函数的直接调用位置。然后可以顺序按照下面四条规则来判断this
的绑定对象:
- 普通函数调用: 在严格模式下绑定到
undefined
,否则绑定到全局对象。 - 对象上的函数调用:绑定到调用的那个对象
- 由
new
调用:绑定到新创建的对象(实例),注意:显示return
函数或对象,返回值不是新创建的对象,而是显式返回的函数或对象。 - 由
call
或apply
、bind
调用:绑定到指定的对象。(严格模式下,绑定到指定的第一个参数。非严格模式下,null
和undefined
,指向全局对象(浏览器中是window
),其余值指向被new Object()
包装的对象。) - 箭头函数不使用上面的绑定规则,根据外层作用域来决定
this
,继承外层非箭头函数调用的this
绑定。 DOM
事件处理函数:一般指向绑定事件的DOM
元素,但有些情况绑定到全局对象(比如IE6~IE8
的attachEvent
)- HTML标签内联事件处理函数:指向所在的dom元素
- JQuery:在许多情况下都指向DOM元素节点
参考文章
JavaScript深入之从ECMAScript规范解读this
面试官问:JS的this指向https://juejin.cn/post/6844903746984476686#heading-5)